首先,我们需要创建一个基于 META-INF/persistence.xml 的EntityManagerFactory 作为我们的持久化单元。它包含持久化单元中所有的类和一些供应商特定的配置。

    我们将使用一个简单的实体作为测试,其中包含有一个 id 和 String 类型的 value 属性,这也将会被持久化。在允许测试之前,我们创建一个实体并且保存它。

    1. Map<String, Object> variables = new HashMap<String, Object>();
    2. variables.put("entityToUpdate", entityToUpdate);
    3. ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("UpdateJPAValuesProcess", variables);

    在我们的流程定义的第一个节点包含一个 serviceTask 将调用方法 在 entityToUpdatesetValue,它解析为 JPA 变量,我们启动流程实例并将从相关联的当前引擎的上下文“EntityManager”进行加载。

    当 service-task 完成后,流程实例等在流程定义中定义的 userTask,这使我们能够检查流程实例。在这一点上,EntityManager 已刷新并更改到实体已经被推到数据库。当我们得到变量 entityToUpdate 值时,它再次加载,我们得到实体,并将实体中的属性 value 设置到 updatedValue

    1. // Servicetask in process 'UpdateJPAValuesProcess' should have set value on entityToUpdate.
    2. Object updatedEntity = runtimeService.getVariable(processInstance.getId(), "entityToUpdate");
    3. assertTrue(updatedEntity instanceof FieldAccessJPAEntity);
    4. assertEquals("updatedValue", ((FieldAccessJPAEntity)updatedEntity).getValue());

    JPASpringTest 例子可以在 activiti-spring-examples 中找到。它的使用场景如下:

    • 存在一个使用 JPA 实体的 Spring bean,用于存储贷款请求。
    • 使用 Activiti,我们可以利用那些由现有的 bean 获得的已有的实体,并将其作为变量在流程中使用。
      流程是按以下步骤进行定义的:
      • 服务任务,利用已有的 LoanRequestBean 使用启动流程时接收的变量(比如,可以来源于开始的表单)来创建新的 LoanRequest。使用 activiti:resultVariable(它以一个变量来存储表达式结果)将创建出来的实体以变量进行存储。
      • 用户任务,让经理查看请求,批准/不批准,结果存储到一个 boolean 类型的变量 approvedByManager 上。
      • 服务任务,用来更新贷款请求实体以使该实体与流程同步。
      • 根据实体属性 approved 的值,利用排他分支来决定下一步选择哪条路径:当请求被批准时,流程将结束;否则,会另有一个任务(发送拒绝信),这样就可以由拒绝信手动地通知用户了。

    请注意此流程不包含任何表单,所以其只用于单元测试

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xmlns:activiti="http://activiti.org/bpmn"
    5. targetNamespace="org.activiti.examples">
    6. <process id="LoanRequestProcess" name="Process creating and handling loan request">
    7. <startEvent id='theStart' />
    8. <sequenceFlow id='flow1' sourceRef='theStart' targetRef='createLoanRequest' />
    9. <serviceTask id='createLoanRequest' name='Create loan request'
    10. activiti:expression="${loanRequestBean.newLoanRequest(customerName, amount)}"
    11. activiti:resultVariable="loanRequest"/>
    12. <sequenceFlow id='flow2' sourceRef='createLoanRequest' targetRef='approveTask' />
    13. <sequenceFlow id='flow3' sourceRef='approveTask' targetRef='approveOrDissaprove' />
    14. <serviceTask id='approveOrDissaprove' name='Store decision'
    15. <sequenceFlow id='flow4' sourceRef='approveOrDissaprove' targetRef='exclusiveGw' />
    16. <exclusiveGateway id="exclusiveGw" name="Exclusive Gateway approval" />
    17. <sequenceFlow id="endFlow1" sourceRef="exclusiveGw" targetRef="theEnd">
    18. <conditionExpression xsi:type="tFormalExpression">${loanRequest.approved}</conditionExpression>
    19. </sequenceFlow>
    20. <sequenceFlow id="endFlow2" sourceRef="exclusiveGw" targetRef="sendRejectionLetter">
    21. <conditionExpression xsi:type="tFormalExpression">${!loanRequest.approved}</conditionExpression>
    22. </sequenceFlow>
    23. <userTask id="sendRejectionLetter" name="Send rejection letter" />
    24. <sequenceFlow id='flow5' sourceRef='sendRejectionLetter' targetRef='theOtherEnd' />
    25. <endEvent id='theEnd' />
    26. <endEvent id='theOtherEnd' />
    27. </process>
    28. </definitions>

    虽然上面的例子很简单,但确实展示出了结合 Spring 和参数化方法表达式来使用 JPA 的强大。此流程根本不需要定制 java
    代码(当然了,除 Spring bean 外),大大加速了部署